import { ExcalidrawElement } from './element/types'

import { newElementWith } from './element/mutateElement'
import { getMaximumGroups } from './groups'
import { getCommonBoundingBox } from './element/bounds'

export interface Distribution {
  space: 'between'
  axis: 'x' | 'y'
}

export const distributeElements = (
  selectedElements: ExcalidrawElement[],
  distribution: Distribution,
): ExcalidrawElement[] => {
  const [start, mid, end, extent] =
    distribution.axis === 'x'
      ? (['minX', 'midX', 'maxX', 'width'] as const)
      : (['minY', 'midY', 'maxY', 'height'] as const)

  const bounds = getCommonBoundingBox(selectedElements)
  const groups = getMaximumGroups(selectedElements)
    .map((group) => [group, getCommonBoundingBox(group)] as const)
    .sort((a, b) => a[1][mid] - b[1][mid])

  let span = 0
  for (const group of groups) {
    span += group[1][extent]
  }

  const step = (bounds[extent] - span) / (groups.length - 1)

  if (step < 0) {
    // If we have a negative step, we'll need to distribute from centers
    // rather than from gaps. Buckle up, this is a weird one.

    // Get indices of boxes that define start and end of our bounding box
    const index0 = groups.findIndex((g) => g[1][start] === bounds[start])
    const index1 = groups.findIndex((g) => g[1][end] === bounds[end])

    // Get our step, based on the distance between the center points of our
    // start and end boxes
    const step =
      (groups[index1][1][mid] - groups[index0][1][mid]) / (groups.length - 1)

    let pos = groups[index0][1][mid]

    return groups.flatMap(([group, box], index) => {
      const translation = {
        x: 0,
        y: 0,
      }

      // Don't move our start and end boxes
      if (index !== index0 && index !== index1) {
        pos += step
        translation[distribution.axis] = pos - box[mid]
      }

      return group.map((element) =>
        newElementWith(element, {
          x: element.x + translation.x,
          y: element.y + translation.y,
        }),
      )
    })
  }

  // Distribute from gaps

  let pos = bounds[start]

  return groups.flatMap(([group, box]) => {
    const translation = {
      x: 0,
      y: 0,
    }

    translation[distribution.axis] = pos - box[start]

    pos += step
    pos += box[extent]

    return group.map((element) =>
      newElementWith(element, {
        x: element.x + translation.x,
        y: element.y + translation.y,
      }),
    )
  })
}
